package com.example.springbootmybatisdemo.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.example.springbootmybatisdemo.dao.ShiroRoleMapper;
import com.example.springbootmybatisdemo.model.ShiroRole;
import com.example.springbootmybatisdemo.model.ShiroUser;
import com.example.springbootmybatisdemo.service.ShiroUserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.*;

/**
 * @Description TODO
 * @Author: cdd
 * @Date: 2020/7/3 10:03
 * @Version: 1.0
 */
@Configuration
public class ShiroConfig {

    @Resource
    private ShiroUserService shiroUserService;

    @Resource
    private ShiroRoleMapper shiroRoleMapper;

    // shiro的过滤器链
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/toLogin");   // 登录页面，请求被拦截后会重定向到登录页面
//        shiroFilterFactoryBean.setSuccessUrl("/index");   // 这个是附件配置，一般情况不生效，原因看源码
//        shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");  // 跳转到没有权限的页面

        // 过滤器链，从上到下，按顺序优先适配
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/logout", "logout");  // shiro内部已经实现了logout
        filterChainDefinitionMap.put("/captcha_img", "anon");

        //静态文件不拦截
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/layui/**", "anon");

        //主要这行代码必须放在所有权限设置的最后，不然会导致所有 url 都被拦截 剩余的都需要认证
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    //  shiro的安全管理中心
    @Bean
    public SecurityManager securityManager(AuthorizingRealm customizeRealm, CacheManager shiroRedisCacheManager, SessionManager sessionManager) {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(customizeRealm);
        defaultSecurityManager.setCacheManager(shiroRedisCacheManager);
        defaultSecurityManager.setSessionManager(sessionManager);
        return defaultSecurityManager;
    }

    @Bean
    public DefaultSessionManager sessionManager(SessionDAO sessionDAO){
        SimpleCookie simpleCookie = new SimpleCookie("shiro.sesssion");
        simpleCookie.setMaxAge(6000);

        DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
        defaultWebSessionManager.setGlobalSessionTimeout(36000000);
        defaultWebSessionManager.setSessionIdCookie(simpleCookie);
        defaultWebSessionManager.setSessionDAO(sessionDAO);
        return defaultWebSessionManager;
    }

    // 自定义域
    @Bean
    public AuthorizingRealm customizeRealm(){
        AuthorizingRealm customizeRealm = new AuthorizingRealm() {
            // 授权处理
            @Override
            protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
                SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
                Set<String> stringSet = new HashSet<>();

                String username = (String) SecurityUtils.getSubject().getPrincipal();
                ShiroUser user = shiroUserService.findUserByUserName(username);
                // 把用户的角色都存到权限里（这里只是为了做演示）
                List<ShiroRole> roleList = shiroRoleMapper.list(user.getId());
                roleList.forEach(r -> stringSet.add("role:" + r.getRoleName()));
                info.setStringPermissions(stringSet);
                return info;
            }

            // 认证处理
            @Override
            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
                String userName = (String) token.getPrincipal();
                String userPwd = new String((char[]) token.getCredentials());
                //根据用户名从数据库查询用户
                ShiroUser user = shiroUserService.findUserByUserName(userName);
                if (user == null) {
                    throw new AccountException("用户名不正确");
                } else if (!userPwd.equals(user.getPassword().toString())) {
                    throw new AccountException("密码不正确");
                }
                return new SimpleAuthenticationInfo(userName, user.getPassword().toString(), getName());
            }
        };
        return customizeRealm;
    }

    // 实现spirng的自动代理
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    // 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持;
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    // 使得shiro标签能在前端页面使用
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

}
